<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ current_path | default(value="文件列表") }}</title>
    <script src="/assets/tailwind.js"></script>
    <script src="/assets/spark-md5.min.js"></script>
    <link href="/assets/fontawesome-free-6.7.2-web/css/all.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#4080FF',
                        success: '#00B42A',
                        warning: '#FF7D00',
                        danger: '#F53F3F',
                        dark: '#1D2129',
                        light: '#F2F3F5',
                    },
                    fontFamily: {
                        inter: ['Inter', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .bg-gradient-blue {
                background: linear-gradient(135deg, #165DFF 0%, #4080FF 100%);
            }
            .shadow-card {
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            }
            .transition-all-300 {
                transition: all 0.3s ease;
            }
            .tab-active {
                @apply border-primary text-primary;
            }
            .table-row-hover {
                @apply hover:bg-gray-50 transition-all-300;
            }
            .drop-area-active {
                @apply border-primary bg-primary/5;
            }
            .file-tag {
                @apply bg-gray-100 px-3 py-1.5 rounded-full text-sm text-gray-700 transition-all-300 hover:bg-gray-200;
            }
            .btn-disabled {
                @apply bg-gray-300 text-gray-500 cursor-not-allowed hover:bg-gray-300 shadow-none;
            }
            .toast-container {
                @apply fixed bottom-6 right-6 z-50 flex flex-col gap-3;
            }
            .toast-message {
                @apply bg-success text-white px-4 py-3 rounded-lg shadow-lg flex items-center max-w-md w-full transform transition-all duration-300;
            }
        }
    </style>
</head>
<body class="bg-gray-50 font-inter text-dark min-h-screen flex flex-col">
    <!-- 顶部导航栏 -->
    <header class="bg-white shadow-sm sticky top-0 z-50">
        <div class="container mx-auto px-4 py-3 flex items-center justify-between">
            <div class="flex items-center space-x-2">
                <i class="fa fa-folder text-primary text-xl"></i>
                <h1 class="text-xl font-bold text-primary">{{ current_path | default(value="文件列表") }}</h1>
            </div>
            <nav class="hidden md:flex items-center space-x-6">
                <a href="showlovewall.html" class="text-gray-600 hover:text-primary transition-all-300 flex items-center">
                    <i class="fa fa-heartbeat mr-2"></i> 表白墙
                </a>
                <a href="#" class="text-primary font-medium flex items-center">
                    <i class="fa fa-tachometer mr-2"></i> 二维码
                </a>
                <a href="/system-settings" class="text-gray-600 hover:text-primary transition-all-300 flex items-center">
                    <i class="fa fa-user-circle mr-2"></i> 系统设置
                </a>
            </nav>
            <div class="flex items-center space-x-4">
                <a href="/logs" class="text-gray-600 hover:text-primary transition-all duration-300 inline-block">
                    <i class="fa fa-bell text-xl"></i>
                </a>

                <button class="md:hidden text-gray-600 hover:text-primary transition-all-300">
                    <i class="fa fa-bars text-xl"></i>
                </button>
            </div>
        </div>
    </header>

    <!-- 主要内容区 -->
    <main class="flex-grow container mx-auto px-4 py-8">
        <div class="max-w-5xl mx-auto">
            <div class="mb-8">
                <h2 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-dark mb-2">二维码</h2>
                <p class="text-gray-500">二维码展示</p>
            </div>

            <!-- 手机访问本网站 -->
            <div class="bg-white rounded-xl shadow-card p-6 mb-8">
                <h3 class="text-xl font-bold mb-4">手机访问本网站</h3>
                <div class="qrcode-container">
                    <img src="{{ qrcode_base64 }}" alt="访问网站的QR码" class="max-w-full h-auto border border-gray-300 rounded-md w-48 mx-auto" title="{{ qr_url }}">
                </div>
            </div>

        </div>
    </main>

    <!-- 页脚 -->
    <footer class="bg-white border-t border-gray-200 py-6">
        <div class="container mx-auto px-4">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <p class="text-gray-500 text-sm">&copy; 2025 北京椰子树信息技术有限公司. 保留所有权利.</p>
                </div>
                <div class="flex space-x-6">

                    <a href="showlovewall.html" class="text-gray-600 hover:text-primary transition-all-300 flex items-center">
                        <i class="fa fa-heartbeat mr-2"></i> 表白墙
                    </a>
                    <a href="/system-settings" class="text-gray-600 hover:text-primary transition-all-300 flex items-center">
                        <i class="fa fa-user-circle mr-2"></i> 系统设置
                    </a>
                    <a href="http://www.coconet.cn" class="text-gray-500 hover:text-primary transition-all-300">
                        <i class="fa fa-question-circle mr-1"></i> 帮助中心
                    </a>
                    <a href="http://www.coconet.cn" class="text-gray-500 hover:text-primary transition-all-300">
                        <i class="fa fa-file-text-o mr-1"></i> 隐私政策
                    </a>
                    <a href="mailto:goodput3@qq.com" class="text-gray-500 hover:text-primary transition-all-300">
                        <i class="fa fa-phone mr-1"></i> 联系我们
                    </a>
                </div>
            </div>
        </div>
    </footer>

    <!-- 提示框容器 -->
    <div id="toastContainer" class="toast-container"></div>

    <script>
        // 管理员下拉菜单逻辑
        const dropdownBtn = document.getElementById('adminDropdownBtn');
        const dropdownMenu = document.getElementById('adminDropdownMenu');
      
        dropdownBtn.addEventListener('click', () => {
            dropdownMenu.classList.toggle('hidden');
            dropdownBtn.querySelector('.fa-angle-down').classList.toggle('rotate-180');
        });
      
        window.addEventListener('click', (event) => {
            if (!event.target.closest('#adminDropdownBtn, #adminDropdownMenu')) {
                dropdownMenu.classList.add('hidden');
                dropdownBtn.querySelector('.fa-angle-down').classList.remove('rotate-180');
            }
        });

        // 上传配置参数
        const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB 分片大小
        const MAX_PARALLEL = 4; // 最大并行上传数
        const UPLOAD_CHUNK_API = "/upload-chunk"; // 分片上传接口
        const MERGE_API = "/merge-chunks"; // 合并接口
        const CHECK_CHUNKS_API = "/check-chunks"; // 断点续传查询接口

        // 全局状态（关键改进：确保所有状态可被正确重置）
        let selectedFiles = []; // 已选择的文件列表（含分片信息）
        let isUploading = false; // 是否正在上传
        let isPreparingFiles = false; // 是否正在准备文件（计算哈希/查分片）
        let speedTimer = null; // 速度计算定时器
        let activeUploads = 0; // 当前活跃上传数（用于判断是否全部完成）

        // 初始化拖拽事件
        function initDragAndDrop() {
            const dropArea = document.getElementById("dropArea");
            
            // 阻止浏览器默认拖放行为
            ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
                dropArea.addEventListener(eventName, preventDefaults, false);
                document.body.addEventListener(eventName, preventDefaults, false);
            });

            // 拖放区域视觉反馈
            ['dragenter', 'dragover'].forEach(eventName => {
                dropArea.addEventListener(eventName, () => {
                    dropArea.classList.add('drop-area-active');
                }, false);
            });

            ['dragleave', 'drop'].forEach(eventName => {
                dropArea.addEventListener(eventName, () => {
                    dropArea.classList.remove('drop-area-active');
                }, false);
            });

            // 处理文件放下事件
            dropArea.addEventListener('drop', handleDrop, false);
        }

        // 阻止默认行为
        function preventDefaults(e) {
            e.preventDefault();
            e.stopPropagation();
        }

        // 处理拖放文件
        function handleDrop(e) {
            e.preventDefault();
            e.stopPropagation();

            const dt = e.dataTransfer;
            let droppedFiles = [];

            // Firefox 兼容：优先从 items 中提取文件
            if (dt.items && dt.items.length > 0) {
                for (let i = 0; i < dt.items.length; i++) {
                    const item = dt.items[i];
                    if (item.kind === 'file') {
                        const file = item.getAsFile();
                        if (file) droppedFiles.push(file);
                    }
                }
                dt.items.clear();
            } else {
                droppedFiles = Array.from(dt.files);
            }

            // 检测是否有有效文件
            if (droppedFiles.length === 0) {
                showDropError('未检测到有效文件，请重新拖放');
                return;
            }

            // 过滤文件夹
            for (const file of droppedFiles) {
                if (file.name.endsWith('/')) {
                    showDropError('不支持上传文件夹，请选择文件');
                    return;
                }
            }

            handleFileSelect(droppedFiles);
        }

        // 显示拖放错误提示
        function showDropError(message) {
            const dropError = document.getElementById("dropError");
            const dropErrorText = document.getElementById("dropErrorText");
            dropErrorText.textContent = message;
            dropError.classList.remove('hidden');
            setTimeout(() => {
                dropError.classList.add('hidden');
            }, 3000);
        }

        // 显示上传成功提示
        function showSuccessToast(filename) {
            let toastContainer = document.getElementById('toastContainer');
            if (!toastContainer) {
                toastContainer = document.createElement('div');
                toastContainer.id = 'toastContainer';
                toastContainer.className = 'toast-container';
                document.body.appendChild(toastContainer);
            }

            const toast = document.createElement('div');
            toast.className = 'toast-message';
            toast.innerHTML = `
                <i class="fa fa-check-circle mr-2 text-xl"></i>
                <span>文件 <strong>${filename}</strong> 上传成功</span>
            `;

            toastContainer.appendChild(toast);
            
            // 3秒后自动移除
            setTimeout(() => {
                toast.style.opacity = '0';
                setTimeout(() => {
                    toast.remove();
                }, 300);
            }, 3000);
        }

        // 处理文件选择
        function handleFileSelect(files) {
            if (files.length === 0) return;
            selectedFiles = []; // 清空原有列表
            isPreparingFiles = true; // 开启文件准备锁
            
            // 更新UI状态
            const selectedContainer = document.getElementById("selectedFilesContainer");
            const uploadBtn = document.getElementById("uploadBtn");
            const progressContainer = document.getElementById("progressContainer");
            
            selectedContainer.classList.remove("hidden");
            document.getElementById("clearFilesBtn").classList.remove("hidden");
            progressContainer.classList.remove("hidden");
            uploadBtn.disabled = true;
            uploadBtn.classList.add("btn-disabled");
            uploadBtn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i> 准备文件中...';
            document.getElementById("totalProgressText").textContent = "正在计算文件信息，请稍候...";

            // 处理每个文件（计算哈希+查分片）
            const filePromises = Array.from(files).map(async (file) => {
                const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
                const fileHash = await calculateFileHash(file);
                const uploadedChunks = await checkUploadedChunks(fileHash, totalChunks);

                const fileInfo = {
                    file: file,
                    fileName: file.name,
                    fileSize: file.size,
                    fileHash: fileHash,
                    totalChunks: totalChunks,
                    uploadedChunks: uploadedChunks,
                    completedChunks: uploadedChunks.length,
                    progress: (uploadedChunks.length / totalChunks) * 100,
                    status: "ready" // ready / uploading / paused / completed / failed
                };
                selectedFiles.push(fileInfo);
                renderFileList();
            });

            // 所有文件准备完成后，启用上传按钮
            Promise.all(filePromises).then(() => {
                isPreparingFiles = false;
                uploadBtn.disabled = false;
                uploadBtn.classList.remove("btn-disabled");
                uploadBtn.innerHTML = '<i class="fa fa-upload mr-2"></i> 开始上传';
                document.getElementById("totalProgressText").textContent = "准备上传...";
                updateTotalProgress();
            }).catch((err) => {
                // 错误处理时强制重置状态（关键改进）
                resetUploadStates();
                showDropError("文件准备失败，请重新选择");
                console.error("文件准备错误：", err);
            });
        }

        // 计算文件哈希
        async function calculateFileHash(file) {
            return new Promise((resolve) => {
                const spark = new SparkMD5.ArrayBuffer();
                const fileReader = new FileReader();
                const chunkSize = 2 * 1024 * 1024; // 2MB 分片读取
                let currentOffset = 0;

                function loadNextChunk() {
                    const start = currentOffset;
                    const end = Math.min(start + chunkSize, file.size);
                    fileReader.readAsArrayBuffer(file.slice(start, end));
                }

                fileReader.onload = (e) => {
                    spark.append(e.target.result);
                    currentOffset += chunkSize;
                    if (currentOffset >= file.size) {
                        resolve(spark.end());
                    } else {
                        loadNextChunk();
                    }
                };

                loadNextChunk();
            });
        }

        // 查询已上传的分片
        async function checkUploadedChunks(fileHash, totalChunks) {
            try {
                const response = await fetch(`${CHECK_CHUNKS_API}?file_hash=${fileHash}`, {
                    method: "GET",
                    credentials: "include"
                });
                if (response.ok) {
                    const data = await response.json();
                    return data.uploaded_chunks || [];
                }
                return [];
            } catch (e) {
                console.error("查询已上传分片失败：", e);
                return [];
            }
        }

        // 切割分片
        function getChunk(file, chunkIndex) {
            const start = chunkIndex * CHUNK_SIZE;
            const end = Math.min(start + CHUNK_SIZE, file.size);
            return file.slice(start, end);
        }

        // 批量开始上传
        async function startBatchUpload() {
            // 状态校验（防止重复点击）
            console.log("start batch upload, ", isPreparingFiles, " len: ", selectedFiles.length, " is uploading: ", isUploading);
            if (isPreparingFiles || selectedFiles.length === 0 || isUploading) return;
            
            // 初始化上传状态
            isUploading = true;
            activeUploads = selectedFiles.length; // 记录活跃上传数
            document.getElementById("uploadBtn").classList.add("hidden");
            document.getElementById("pauseBtn").classList.remove("hidden");
            document.getElementById("totalProgressText").textContent = "正在上传...";

            // 启动速度计算
            startSpeedCalculation();

            // 对每个文件启动上传
            selectedFiles.forEach(fileInfo => {
                if (fileInfo.status !== "uploading") {
                    console.log("uploading file chunks...")
                    fileInfo.status = "uploading";
                    uploadFileChunks(fileInfo);
                }
            });
        }

        // 上传单个文件的所有分片
        async function uploadFileChunks(fileInfo) {
            const { file, fileHash, totalChunks, uploadedChunks } = fileInfo;
            const chunkQueue = [];
            console.log("upload file chunks: ", totalChunks, " uploadedChunks: ", uploadedChunks);
            // 生成未上传的分片队列
            for (let i = 0; i < totalChunks; i++) {
                if (!uploadedChunks.includes(i)) {
                    chunkQueue.push(i);
                }
            }

            // 并行上传控制
            const parallelUpload = async () => {
                console.log("parallelUpload, activeUploads: ", activeUploads);
                // 如果上传已暂停或队列为空，退出
                if (!isUploading || chunkQueue.length === 0) {
                    // 所有分片上传完成，请求合并
                    if (chunkQueue.length === 0) {
                        await mergeFileChunks(fileInfo);
                        fileInfo.status = "completed";
                        updateFileProgress(fileInfo);
                        showSuccessToast(fileInfo.fileName);
                        
                        // 递减活跃上传数，检查是否全部完成
                        activeUploads--;
                        if (activeUploads <= 0) {
                            // 所有文件上传完成，重置状态（关键改进）
                            setTimeout(resetUploadStates, 1000);
                        }
                    }
                    return;
                }

                const chunkIndex = chunkQueue.shift();
                try {
                    await uploadSingleChunk(fileInfo, chunkIndex);
                    // 更新状态
                    fileInfo.completedChunks++;
                    fileInfo.progress = (fileInfo.completedChunks / totalChunks) * 100;
                    fileInfo.uploadedChunks.push(chunkIndex);
                    updateFileProgress(fileInfo);
                    updateTotalProgress();
                } catch (e) {
                    console.error(`分片 ${chunkIndex} 上传失败：`, e);
                    chunkQueue.push(chunkIndex); // 失败重试
                    fileInfo.status = "failed";
                    updateFileProgress(fileInfo);
                    
                    // 上传失败时也需要重置状态（关键改进）
                    activeUploads--;
                    if (activeUploads <= 0) {
                        resetUploadStates();
                    }
                }

                parallelUpload();
            };

            // 启动并行任务
            for (let i = 0; i < Math.min(MAX_PARALLEL, chunkQueue.length); i++) {
                parallelUpload();
            }
        }

        // 上传单个分片
        async function uploadSingleChunk(fileInfo, chunkIndex) {
            const { file, fileHash, totalChunks } = fileInfo;
            const chunk = getChunk(file, chunkIndex);

            const meta = JSON.stringify({
                file_name: file.name,
                file_hash: fileHash,
                chunk_index: chunkIndex,
                total_chunks: totalChunks
            });

            const formData = new FormData();
            formData.append("meta", meta);
            formData.append("chunk_data", chunk, `chunk_${chunkIndex}`);

            const response = await fetch(UPLOAD_CHUNK_API, {
                method: "POST",
                body: formData,
                credentials: "include"
            });

            if (!response.ok) {
                const errorText = await response.text();
                throw new Error(`HTTP 错误：${response.status}，${errorText}`);
            }
        }

        // 合并分片
        async function mergeFileChunks(fileInfo) {
            const { fileName, fileHash, totalChunks } = fileInfo;
            try {
                const response = await fetch(MERGE_API, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify({
                        file_name: fileName,
                        file_hash: fileHash,
                        total_chunks: totalChunks
                    }),
                    credentials: "include"
                });

                if (!response.ok) {
                    const errorText = await response.text();
                    throw new Error(`合并失败：${errorText}`);
                }
            } catch (e) {
                console.error("合并分片失败：", e);
                throw e; // 抛出错误让上层处理
            }
        }

        // 渲染文件列表
        function renderFileList() {
            const container = document.getElementById("selectedFilesContainer");
            container.innerHTML = "";

            selectedFiles.forEach((fileInfo) => {
                const fileSizeStr = formatFileSize(fileInfo.fileSize);
                const fileItem = document.createElement("div");
                fileItem.className = "file-item p-3 border border-neutral-200 rounded-lg";
                fileItem.innerHTML = `
                    <div class="flex justify-between items-center mb-2">
                        <span class="font-medium text-neutral-700">${fileInfo.fileName}</span>
                        <span class="text-sm text-neutral-500">${fileSizeStr} / ${fileInfo.totalChunks}个分片</span>
                    </div>
                    <div class="w-full h-2 bg-neutral-200 rounded-full mb-2">
                        <div class="file-progress h-full bg-primary" style="width: ${fileInfo.progress}%"></div>
                    </div>
                    <div class="flex justify-between text-xs text-neutral-500">
                        <span class="completed-chunks">已上传：${fileInfo.completedChunks}个分片</span>
                        <span class="file-status">状态：${getStatusText(fileInfo.status)}</span>
                    </div>
                `;
                container.appendChild(fileItem);
            });
        }

        // 更新单个文件进度
        function updateFileProgress(fileInfo) {
            const fileItems = document.querySelectorAll(".file-item");
            const index = selectedFiles.indexOf(fileInfo);
            if (index < 0 || index >= fileItems.length) return;

            const fileItem = fileItems[index];
            fileItem.querySelector(".file-progress").style.width = `${fileInfo.progress}%`;
            fileItem.querySelector(".completed-chunks").textContent = `已上传：${fileInfo.completedChunks}个分片`;
            fileItem.querySelector(".file-status").textContent = `状态：${getStatusText(fileInfo.status)}`;
        }

        // 更新整体进度
        function updateTotalProgress() {
            let totalSize = 0;
            let completedSize = 0;

            selectedFiles.forEach(fileInfo => {
                totalSize += fileInfo.fileSize;
                completedSize += (fileInfo.completedChunks / fileInfo.totalChunks) * fileInfo.fileSize;
            });

            const totalProgress = (completedSize / totalSize) * 100;
            document.getElementById("totalProgressBar").style.width = `${totalProgress.toFixed(1)}%`;
            document.getElementById("totalProgressText").textContent = `整体进度：${totalProgress.toFixed(1)}%`;
        }

        // 计算上传速度
        function startSpeedCalculation() {
            // 清除可能存在的旧定时器（关键改进）
            if (speedTimer) clearInterval(speedTimer);
            
            let lastCompletedSize = 0;
            speedTimer = setInterval(() => {
                let currentCompletedSize = 0;
                selectedFiles.forEach(fileInfo => {
                    currentCompletedSize += (fileInfo.completedChunks / fileInfo.totalChunks) * fileInfo.fileSize;
                });

                const speed = (currentCompletedSize - lastCompletedSize) / 1024 / 1024; // MB/s
                document.getElementById("uploadSpeed").textContent = `速度：${speed.toFixed(1)}MB/s`;
                lastCompletedSize = currentCompletedSize;
            }, 1000);
        }

        // 格式化文件大小
        function formatFileSize(size) {
            if (size < 1024) return `${size}B`;
            if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}KB`;
            if (size < 1024 * 1024 * 1024) return `${(size / 1024 / 1024).toFixed(1)}MB`;
            return `${(size / 1024 / 1024 / 1024).toFixed(1)}GB`;
        }

        // 获取状态文本
        function getStatusText(status) {
            const statusMap = {
                ready: "等待上传",
                uploading: "上传中",
                paused: "已暂停",
                completed: "上传完成",
                failed: "上传失败"
            };
            return statusMap[status] || "未知状态";
        }

        // 暂停上传
        function pauseBatchUpload() {
            isUploading = false;
            clearInterval(speedTimer);
            document.getElementById("pauseBtn").classList.add("hidden");
            document.getElementById("uploadBtn").classList.remove("hidden");
            document.getElementById("totalProgressText").textContent = "已暂停";
            selectedFiles.forEach(fileInfo => {
                if (fileInfo.status === "uploading") {
                    fileInfo.status = "paused";
                    updateFileProgress(fileInfo);
                }
            });
        }

        // 清空选择（新增force参数强制重置所有状态）
        function clearSelectedFiles(force = false) {
            selectedFiles = [];
            isPreparingFiles = false;
            isUploading = false; // 强制停止上传
            activeUploads = 0;
            clearInterval(speedTimer);
            
            // 恢复UI初始状态
            const uploadBtn = document.getElementById("uploadBtn");
            document.getElementById("selectedFilesContainer").classList.add("hidden");
            document.getElementById("clearFilesBtn").classList.add("hidden");
            document.getElementById("progressContainer").classList.add("hidden");
            document.getElementById("pauseBtn").classList.add("hidden");
            
            // 恢复上传按钮
            uploadBtn.classList.remove("hidden");
            uploadBtn.disabled = false;
            uploadBtn.classList.remove("btn-disabled");
            uploadBtn.innerHTML = '<i class="fa fa-upload mr-2"></i> 开始上传';
        }

        // 重置上传状态（关键改进：统一处理上传完成/失败后的状态重置）
        function resetUploadStates() {
            isUploading = false;
            clearInterval(speedTimer);
            
            // 恢复按钮状态
            document.getElementById("pauseBtn").classList.add("hidden");
            document.getElementById("uploadBtn").classList.remove("hidden");
            document.getElementById("uploadBtn").disabled = false;
            document.getElementById("uploadBtn").classList.remove("btn-disabled");
            document.getElementById("uploadBtn").innerHTML = '<i class="fa fa-upload mr-2"></i> 开始上传';
            
            // 更新进度文本
            document.getElementById("totalProgressText").textContent = "所有文件上传完成";
            
            // 3秒后刷新页面（可选，根据需求决定是否保留）
            setTimeout(() => window.location.reload(), 3000);
        }

        // 初始化拖拽功能
        document.addEventListener('DOMContentLoaded', initDragAndDrop);
    </script>
</body>
</html>
